home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
3_0
/
FILEFORM
/
FILEFORM.C
next >
Wrap
Text File
|
1988-07-29
|
15KB
|
601 lines
/*
stylefile.c -- C source code that defines (or more accurately proposes) a file format
standard for styled text that can be edited using the newer version of Apple's Text
Edit with selective styling, font changes and color changes.
Developed to compile under THINK's Lightspeed C version 3.X, but should run fine under
earlier version of LSC, and probably MPW╔
The basic file structure is:
Length Meaning
------ -------
40 bytes file header, see declaration below
textlength bytes the un-styled ASCII text
stylelength bytes contents of a StScrpHandle as returned by GetStylScrap
Since every application that uses styled text probably has to save the same kind of
information on disk, doesn't it make sense for all of us to do it the same way?
This format would be equivalent to the PICT file format for pictures. Perhaps a good
name for the format would be STXT for Styled TeXT?
Note that in the file header we leave room for four-byte selection offsets, and for
lengths of the buffer text and the StScrpHandle. In today's Text Edit, only two bytes
are needed, but it probably is smart to waste a few bytes in case Apple ever really
soups up TE to handle truly large blocks of text.
The following code, written to be compiled by Lightspeed C leaves no uncertainty about
how the file format can be implemented.
If anyone is interested in discussing this proposal, please leave a message on CompuServe's
APPDEV Forum.
Dave Winer 76244,120
July 3, 1988 -- Release 2
-------------------------
Added several features to the header, at the suggestion of Chris Wakefield [CompuServe
76337,335].
systemversionnumber is an integer that tells us what version of the system created
the STXT file. See IM-V pp 5-8.
vertscrollposition is a long that tells you how to initialize your vertical scrollbar
so that it scrolls to the same position it was at when the STXT file was created. The
Control Manager only deals with ints, but we made this a long to reserve space for
growth. You can ignore this if you don't dispay a vertical scrollbar, but at least
set it to 0 when you save.
textoffset and styleoffset, both longs, say where the text and the style record are
stored in the file. This might allow you to store some other kinds of information
in different places that will be ignored by programs reading STXT format.
windowrect says where the window that holds the text should be placed, in global
coordinates. If you don't save a real value here, be sure that windowrect.bottom
is 0 -- it's probably safest to set all the fields of windowrect to 0.
Thanks Chris for some excellent suggestions!
A note -- even if a "standard" is never set, you can still write programs that read
and write this file format. I think you'll end up being happy that you did because
it may give you some extra compatibility with some neat software products.
No promises!
DW
July 29, 1988 -- Release 3
--------------------------
At the suggestion of David Dunham, author of Acta and MiniWriter, I have cleaned up
a few of the initializations in initeditbuffer. I've also learned a little more about
TE and changed a couple of things at my own suggestion.
1. Took out the code that inserts a blank, selects it, sets its style to the default
and then deletes it. This was voodoo, apparently very unnecessary.
2. Instead of copying directly into the TEHandle's selStart and selEnd fields, it now
uses TESetSelect as the exclusive way of working with these fields. In general, you're
supposed to use the procedural interface whenever one is provided.
3. Included a routine that you might find generally useful: getscraphandle.
4. Also put things in registers wherever possible. This greatly reduces codesize!
5. No changes to the file format, so currentversion stays at 2.
*/
#include "standard.h"
#define currentversion 2
#define fname "\pSTXT Test File"
typedef struct {
int versionnumber; /*makes it easy to upgrade the format*/
int systemversionnumber; /*what SysEnvirons returns, -1 indicates no version available*/
long startsel, endsel; /*leave room a buffer of greater than 32767 characters*/
long vertscrollposition; /*value for vertical scroll bar controlhandle*/
long textoffset, textlength; /*where the text is stored, and its length*/
long styleoffset, stylelength; /*where the style record is stored, and its length*/
Rect windowrect; /*optional, the size of the window, if not set, set bottom to 0*/
} tystyleheader;
TEHandle editbuffer; /*the place where STXT files are saved from and loaded into*/
Rect editrect; /*the rectangle where text is displayed*/
WindowPtr editwindow; /*the window that the edit buffer is displayed in*/
#define automaticfont geneva
#define automaticsize 12
#define automaticface 0
int getsystemversion () {
SysEnvRec theworld;
if (SysEnvirons (1, &theworld) != noErr) /*error, indicate no version number available*/
return (-1);
return (theworld.systemVersion);
} /*getsystemversion*/
boolean filewrite (fnum, ctwrite, bufptr) int fnum; long ctwrite; char *bufptr; {
/*
write ctwrite bytes from bufptr to file indicated by fnum at
the FPos of the file.
*/
return (FSWrite (fnum, &ctwrite, bufptr) == noErr);
} /*filewrite*/
boolean fileseek (fnum, pos) int fnum; long pos; {
return (SetFPos (fnum, fsFromStart, pos) == noErr);
} /*fileseek*/
boolean fileread (fnum, pos, ctread, bufptr) int fnum; long pos, ctread; char *bufptr; {
/*
read ctread bytes from the file indicated by fnum into bufptr
at file position pos.
*/
if (!fileseek (fnum, pos))
return (false);
return (FSRead (fnum, &ctread, bufptr) == noErr);
} /*fileread*/
boolean initeditbuffer () {
/*
create a new TEHandle in editbuffer.
Apple Technote #131 says that the current port and font, size and style are
important when TEStylNew is called.
editrect and editwindow must be set before this routine is called.
*/
TextStyle newstyle;
SetPort (editwindow); /*TEStylNew makes a copy of the current port*/
TextFont (automaticfont); /*see Apple Technote #131*/
TextSize (automaticsize);
TextFace (automaticface);
editbuffer = TEStylNew (&editrect, &editrect);
if (editbuffer == nil) /*allocation failed*/
return (false);
TECalText (editbuffer); /*see Apple Technote #131*/
TESetSelect (0, 0, editbuffer); /*get ready to set the nullstyle*/
newstyle.tsFont = automaticfont;
newstyle.tsSize = automaticsize;
newstyle.tsFace = automaticface;
TESetStyle (
doFont + doFace + doSize, &newstyle, true, editbuffer); /*see IM-V p 264*/
return (true);
} /*initeditbuffer*/
boolean validfileheader (header) tystyleheader header; {
/*
put the file header through a vigorous reality check.
*/
if (header.versionnumber != currentversion)
return (false);
if ((header.endsel < 0) || (header.endsel > header.textlength))
return (false);
if (header.textlength < 0)
return (false);
if (header.stylelength < 0)
return (false);
return (true); /*passed all tests!!!*/
} /*validfileheader*/
boolean loadstylerecord (fnum) int fnum; {
/*
The caller wants us to read the contents of editbuffer from the open file "fnum".
This effectively insulates the caller from detailed knowledge of both the structure
of the stylerecord and the details of our file format. It also makes it easy for
a compound style of document, that might consist of many styled buffers, to be saved.
If we return true then the operation went smoothly. If we return false, there
was an error in reading from disk.
*/
tystyleheader header;
Handle htmp;
StScrpHandle hstyle;
htmp = nil; /*if there's an error it won't get disposed*/
if (!fileread (fnum, (long) 0, (long) sizeof (header), &header))
goto error;
if (!validfileheader (header)) /*failed the reality check*/
goto error;
if (!initeditbuffer ()) /*sets up new editbuffer*/
goto error;
htmp = NewHandle (header.textlength); /*allocate temporary buffer*/
if (htmp == nil) /*memory allocation error*/
goto error;
HLock (htmp);
if (!fileread (fnum, header.textoffset, header.textlength, *htmp)) {
HUnlock (htmp);
goto error;
}
HUnlock (htmp);
hstyle = nil; /*indicate that there is no style handle*/
if (header.stylelength > 0) {
hstyle = (StScrpHandle) NewHandle (header.stylelength);
if (hstyle == nil) /*memory allocation error*/
goto error;
HLock ((Handle) hstyle);
if (!fileread (fnum, header.styleoffset, header.stylelength, *hstyle)) {
HUnlock ((Handle) hstyle);
goto error;
}
HUnlock ((Handle) hstyle);
} /*reading style header from disk*/
if ((hstyle != nil) && (header.textlength > 0)) {
HLock (htmp);
TEStylInsert (*htmp, header.textlength, hstyle, editbuffer);
HUnlock (htmp);
}
if (hstyle != nil)
DisposHandle (hstyle);
if (htmp != nil)
DisposHandle (htmp); /*get rid of temporary buffer*/
TEUpdate (&editrect, editbuffer);
TESetSelect (header.startsel, header.endsel, editbuffer);
TEActivate (editbuffer);
return (true); /*the load was successful in every respect*/
error: /*jump to here to clean up and return false*/
if (hstyle != nil)
DisposHandle (hstyle);
if (htmp != nil)
DisposHandle (htmp);
return (false);
} /*loadstylerecord*/
StScrpHandle getscraphandle (buffer) TEHandle buffer; {
/*
Coax TE into giving us a scrap handle for buffer. This involves
selecting the whole buffer, calling GetStylScrap and restoring the
selection.
*/
register int origstart, origend;
register StScrpHandle hstyle;
origstart = (**buffer).selStart; /*save*/
origend = (**buffer).selEnd;
TESetSelect (0, (**buffer).teLength, buffer); /*select the whole thing*/
hstyle = GetStylScrap (buffer);
TESetSelect (origstart, origend, buffer); /*restore*/
return (hstyle);
} /*getscraphandle*/
boolean savestylerecord (fnum) int fnum; {
/*
The caller wants us to write the contents of editbuffer out to the open file "fnum".
This effectively insulates the caller from detailed knowledge of both the structure
of the stylerecord and the details of our file format. It also makes it easy for
a compound style of document, that might consist of many stylerecords, to be saved.
If we return true then the operation went smoothly. If we return false, there
was an error in writing to disk.
*/
register StScrpHandle hstyle;
register long ct;
register Handle h;
tystyleheader header;
/*organize information into the header record*/
header.versionnumber = currentversion;
header.systemversionnumber = getsystemversion ();
header.vertscrollposition = 0; /*set to GetCtlValue (your vert scrollbar)*/
SetRect (&header.windowrect, 0, 0, 0, 0);
header.startsel = (**editbuffer).selStart;
header.endsel = (**editbuffer).selEnd;
header.textoffset = sizeof (header); /*starts immediately after header*/
header.textlength = (**editbuffer).teLength;
header.styleoffset = header.textoffset + header.textlength;
hstyle = getscraphandle (editbuffer);
if (hstyle == nil)
header.stylelength = 0;
else
header.stylelength = GetHandleSize ((Handle) hstyle);
/*reserach phase is over, start writing stuff to the file*/
if (!filewrite (fnum, (long) sizeof (header), &header)) { /*write out the header*/
if (hstyle != nil)
DisposHandle (hstyle);
return (false);
}
if (header.textlength > 0) { /*there actually is some text in the buffer*/
HLock (h = (**editbuffer).hText); /*we pass a pointer, not a handle*/
if (!filewrite (fnum, header.textlength, *h)) { /*write out the text buffer*/
HUnlock (h);
if (hstyle != nil)
DisposHandle (hstyle);
return (false);
}
HUnlock (h);
}
if (header.stylelength > 0) { /*there actually is some style information*/
HLock (hstyle); /*must lock it down since we have to pass a pointer, not a handle*/
if (!filewrite (fnum, header.stylelength, *hstyle)) {
HUnlock (h);
if (hstyle != nil)
DisposHandle (hstyle);
return (false);
}
HUnlock (hstyle);
}
if (hstyle != nil)
DisposHandle (hstyle);
return (true); /*the save was successful*/
} /*savestylerecord*/
initmacintosh () {
/*
the magic stuff that every macintosh application needs to do
before doing anything else.
*/
register int i;
MaxApplZone ();
for (i = 1; i < 11; i++)
MoreMasters ();
InitGraf (&thePort);
InitFonts ();
FlushEvents (everyEvent, 0);
InitWindows ();
InitMenus ();
TEInit ();
InitDialogs (nil);
InitCursor();
} /*initmacintosh*/
setupwindow () {
/*
set up the window that the program runs inside of.
the window is sized to be slightly smaller that the
screen you're using.
*/
Rect r;
r = screenBits.bounds;
r.top += 40;
r.bottom -= 10;
r.left += 10;
r.right -= 10;
editwindow = NewWindow (nil, &r, fname, TRUE, documentProc, -1, FALSE, 0);
SetPort (editwindow);
} /*setupwindow*/
typedef AppFile **hdlappfile;
int getappvnum () {
/*
get me the volume id of the application that's running.
useful for opening data files that are located in the same folder as
the application itself.
*/
bigstring bsname;
int refnum;
Handle hfinderparams;
GetAppParms (bsname, &refnum, &hfinderparams);
return ((**((hdlappfile) hfinderparams)).vRefNum);
} /*getappvnum*/
main () {
int vnum, fnum;
OSErr errcode;
initmacintosh ();
setupwindow (); /*creates a new window pointed to by editwindow*/
editrect = (*editwindow).portRect;
InsetRect (&editrect, 10, 10);
vnum = getappvnum (); /*we're going to look in same folder that the program is in*/
errcode = FSOpen (fname, vnum, &fnum);
if (errcode != noErr) {
sysbeep; /*listen for a single sysbeep*/
return;
}
if (!loadstylerecord (fnum)) {
sysbeep; sysbeep; /*listen for a double sysbeep*/
FSClose (fnum);
return;
}
while (!Button ()) /*press the mouse button to exit*/
SystemTask (); /*keep TOPS, etc. active*/
errcode = SetFPos (fnum, fsFromStart, 0); /*reset to the beginning of the file*/
if (errcode != noErr) {
sysbeep; /*listen for a single sysbeep*/
FSClose (fnum);
return;
}
if (!savestylerecord (fnum)) {
sysbeep; sysbeep; /*listen for a double sysbeep*/
}
DisposeWindow (editwindow);
FSClose (fnum);
} /*main*/